/*
 * File : spi_flash.c
 *
 */
#include "config.h"
#include "memory.h"
#include "xstring.h"
#include "spi_flash.h"
#include "task.h"
#include "type.h"


/**
 * @def  	SPI_FLASH_CS_LOW
 * @brief	Select SPI FLASH: ChipSelect pin low
 */
#define SPI_FLASH_CS_LOW()	GPIO_ResetBits(GPIOA, GPIO_Pin_4)
/**
 * @def  	SPI_FLASH_CS_HIGH
 * @brief	Deselect SPI FLASH: ChipSelect pin high 
 */
#define SPI_FLASH_CS_HIGH()	GPIO_SetBits(GPIOA, GPIO_Pin_4)

/* flash command define */
#define WREN		0x06	///< Write enable
#define WRDI		0x04	///< Write Disable
#define RDID		0x9F	///< Read identification
#define RDSR		0x05	///< Read Status Register
#define WRSR		0x01	///< Write Status Register
#define WRLR		0xE5	///< Write to Lock Register
#define RDLR		0xE8	///< Read Lock Register
#define READ		0x03	///< Read from Memory
#define FAST_READ	0x0B	///< Read Data Bytes at Higher Speed
#define PW			0x0A	///< Page Write
#define PP			0x02	///< Page Program
#define PE			0x20	///< Page Erase 
#define SE			0xD8	///< Sector Erase 
#define SSE			0x52	///< SubSector Erase
#define BE			0xC7	///< Bulk Erase
#define DP			0xB9	///< Deep Power-down
#define RDP			0xAB	///< Release from Deep Power-down

#define WIP_Flag	0x01	///< Write In Progress (WIP) flag
#define Dummy_Byte	0xA5	///< Dummy byte

/** @name SPI STATUS CODE DEFINE
 * @{
 */
#define SPI_FLASH_READY		0	///< Can read, write, erase right now
#define SPI_FLASH_READ		1	///< flash reading
#define SPI_FLASH_WRITE		2	///< flash writing
#define SPI_FLASH_ERASE		3	///< flash erasing
/** @} */
static unsigned char SPI_Flash_Status;	///< flash status

static unsigned char *gBuf;		///< read/write buffer pointer
static unsigned short gCount;	///< read/write buffer count
static var32 gAddr;				///< read/write Flash address

static unsigned char gWState = 0;	///< write state
static unsigned short gWCount;	///< write count
static unsigned char gWMode;	///< write mode
/* write state define */
#define FLASH_STATE_IDLE	0	///< idle
#define FLASH_STATE_WRITE	1	///< start write
#define FLASH_STATE_CHECK	2	///< check write or erase finish

unsigned short SPI_Flash_TimeOut;

/* Erase Cycle Time Out define */
#define TIMEOUT_FLASH	500

/**
 * @brief	Wait SPI Flash Ready to do next action
 * @param	none
 * @return	SPI_FLASH_OK  : ready
 *			SPI_FLASH_ERR : timeout
 * @note	Sector erase/Bulk erase no support!!
 */
unsigned char SPI_Flash_Wait_Ready(void)
{
	unsigned long temp = gSystick;
	unsigned long timeout;
	
	while (SPI_Flash_Status != SPI_FLASH_READY)
	{
		timeout =  (gSystick - temp);
		if (timeout > TIMEOUT_FLASH)
		{
			*pFlag_err_flash_timeout = 1;
			return SPI_FLASH_ERR;
		}
	}
	return SPI_FLASH_OK;
}


/**
 * @brief	Send a byte to flash
 * @param	byte	data
 * @return	read data from SPI after send one byte
 * @note
 */
static unsigned char SPI_FLASH_SendByte(unsigned char byte)
{
	unsigned long temp;
	unsigned long timeout;
	
	temp = gSystick;
	/* Loop while DR register in not emplty */
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
	{
		timeout = gSystick - temp;
		if (timeout > 10)
		{
			*pFlag_err_flash = 1;
			return SPI_FLASH_ERR;
		}
	}
	/* Send byte through the SPI1 peripheral */
	SPI_I2S_SendData(SPI1, byte);
	/* Wait to receive a byte */
	temp = gSystick;
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
	{
		timeout = gSystick - temp;
		if (timeout > 10)
		{
			*pFlag_err_flash = 1;
			return SPI_FLASH_ERR;
		}
	}
	/* Return the byte read from the SPI bus */
	return SPI_I2S_ReceiveData(SPI1);
}

/**
 * @brief	Send Write Enable command to flash
 * @param	none
 * @return	none
 * @note
 */
static void SPI_FLASH_WriteEnable(void)
{
	SPI_FLASH_CS_LOW();
	SPI_FLASH_SendByte(WREN);
	SPI_FLASH_CS_HIGH();
}

/**
 * @brief	Send Wake UP command to flash
 * @param	none
 * @return	none
 * @note
 */
static void SPI_Flash_WakeUP(void)
{
	SPI_FLASH_CS_LOW();
	SPI_FLASH_SendByte(RDP);
	SPI_FLASH_CS_HIGH();
}

/**
 * @brief	Send Deep sleep command to flash
 * @param	none
 * @return	none
 * @note
 */
static void SPI_Flash_Sleep(void)
{
	SPI_FLASH_CS_LOW();
	SPI_FLASH_SendByte(DP);
	SPI_FLASH_CS_HIGH();
}

/**
 * @brief	Erase Flash
 * @param	cmd			PE/SE/SSE/BE
 * @param	addr		Flash address
 * @return	none
 * @note
 */
static void SPI_Flash_Erase(unsigned char cmd, unsigned long addr)
{
	var32 address;
	
	SPI_Flash_Status = SPI_FLASH_ERASE;

	*pFlag_pwr_flash = 1;
	address.u32 = addr;

	SPI_FLASH_WriteEnable();

	SPI_FLASH_CS_LOW();
	SPI_FLASH_SendByte(cmd);
	
	SPI_FLASH_SendByte(address.u8.b2);
	SPI_FLASH_SendByte(address.u8.b1);
	SPI_FLASH_SendByte(address.u8.b0);

	SPI_FLASH_CS_HIGH();
	gWCount = 0;
	gWState = FLASH_STATE_CHECK;
}

/**
 * @brief	flash write task
 * @param	none
 * @return	none
 * @note
 */
void SPI_Flash_Hook(void)
{
	unsigned short page_remain;
	unsigned char status;
	
	switch (gWState)
	{
	default:
	case FLASH_STATE_IDLE:
		break;
	case FLASH_STATE_WRITE:
		*pFlag_pwr_flash = 1;
		/* Start write a page */
		*pFlag_spi_busy	= 1;
		page_remain		= 256 - gAddr.u8.b0;
		if (gWCount > page_remain)
		{
			gCount	= page_remain;
			gWCount	-= page_remain;
		} else {
			gCount	= gWCount;
			gWCount	= 0;
		}
		SPI_FLASH_CS_HIGH();
		SPI_FLASH_WriteEnable();
		SPI_FLASH_CS_LOW();
		SPI_FLASH_SendByte(gWMode);
		SPI_FLASH_SendByte(gAddr.u8.b2);
		SPI_FLASH_SendByte(gAddr.u8.b1);
		SPI_FLASH_SendByte(gAddr.u8.b0);
		SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, ENABLE);
		gAddr.u32 += page_remain;
		gWState = FLASH_STATE_CHECK;
		break;
	case FLASH_STATE_CHECK:
		/* check write finish */
		if (*pFlag_spi_busy == 0)
		{
			/* send finish */
			SPI_FLASH_CS_LOW();
			SPI_FLASH_SendByte(RDSR);
			status = SPI_FLASH_SendByte(Dummy_Byte);
			SPI_FLASH_CS_HIGH();
			if ((status & WIP_Flag) == 0)
			{
				/* write finish */
				if (gWCount == 0)
				{
					/* write finish */
					gWState = FLASH_STATE_IDLE;
					*pFlag_pwr_flash = 0;
					SPI_Flash_Status = SPI_FLASH_READY;
				}
				else
				{
					/* next page */
					gWState = FLASH_STATE_WRITE;
				}
			}
		}
		break;
	}
}

/**
 * @brief	Write data to flash
 * @param	addr	Flash address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
static void SPI_Flash_Write_Init(	unsigned long addr,
									unsigned char *buf,
									unsigned short count)
{
	int i;

	for (; count != 0;)
	{
		if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
		{
			SPI_Flash_Status = SPI_FLASH_WRITE;
			if (count > 256)
				i = 256;
			else
				i = count;
			count -= i;
			xmemcpy((void *)pFlashData, (void *)buf, i);
			gAddr.u32	= addr;
			gBuf		= pFlashData;
			gWCount		= i;
			gWState		= FLASH_STATE_WRITE;
			addr		+= i;
			buf			+= i;
		}
	}
	
}

/** @ingroup SPI_FLASH Cargo SPI_FLASH
 * @{
 */
/**
 * @brief	Read flash ID
 * @param	none
 * @return	Flash ID
 * @note	Return 0 means flash busy.
 */
unsigned long SPI_Flash_ReadID(void)
{
	var32 id;

	id.u32 = 0;
	
	if (SPI_Flash_Status == SPI_FLASH_READY)
	{
		*pFlag_pwr_flash = 1;
		SPI_FLASH_CS_LOW();
		SPI_FLASH_SendByte(RDID);
		id.u8.b2 = SPI_FLASH_SendByte(Dummy_Byte);
		id.u8.b1 = SPI_FLASH_SendByte(Dummy_Byte);
		id.u8.b0 = SPI_FLASH_SendByte(Dummy_Byte);
		SPI_FLASH_CS_HIGH();
		*pFlag_pwr_flash = 0;
		SPI_Flash_Status = SPI_FLASH_READY;
	}
	return id.u32;

}

/**
 * @brief	Page Erase Flash
 * @param	addr		Flash address
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Page_Erase(unsigned long addr)
{
	addr = addr & 0x00FFFF00;
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		SPI_Flash_Erase(PE, addr);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}

/**
 * @brief	SubSector Erase Flash
 * @param	addr		Flash address
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_SubSector_Erase(unsigned long addr)
{
	addr = addr & 0x00FFF000;
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		SPI_Flash_Erase(SSE, addr);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}

/**
 * @brief	Sector Erase Flash
 * @param	addr		Flash address
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Sector_Erase(unsigned long addr)
{
	addr = addr & 0x00FF0000;
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		SPI_Flash_Erase(SE, addr);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}

/**
 * @brief	Bulk Erase Flash
 * @param	addr		Flash address
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Bulk_Erase(void)
{
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		SPI_Flash_Erase(BE, 0);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}

/**
 * @brief	Read data from flash
 * @param	addr	Flash address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Read(	unsigned long addr,
								unsigned char *buf,
								unsigned short count)
{
	var32 address;

	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		*pFlag_pwr_flash = 1;
		address.u32 = addr;
		gBuf		= buf;
		gCount		= count;
		/* change status to READ */	
		SPI_Flash_Status = SPI_FLASH_READ;
		
		SPI_FLASH_CS_HIGH();
		SPI_FLASH_CS_LOW();
		SPI_FLASH_SendByte(READ);
		SPI_FLASH_SendByte(address.u8.b2);
		SPI_FLASH_SendByte(address.u8.b1);
		SPI_FLASH_SendByte(address.u8.b0);
		SPI_I2S_SendData(SPI1, Dummy_Byte);
		/* Enable SPI RXNE interrupt */
		SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}

/**
 * @brief	Write data to flash
 * @param	addr	Flash address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Write(	unsigned long addr,
								unsigned char *buf,
								unsigned short count)
{
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		gWMode = PP;
		SPI_Flash_Write_Init(addr, buf, count);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}
/**
 * @brief	Write data to flash
 * @param	addr	Flash address
 * @param	buf		buffer address
 * @param	count	length (base:byte)
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note	Execute this function will start to erase flash, and
 *			you can check SPI status to check finished or not.
 */
unsigned char SPI_Flash_Fast_Write(	unsigned long addr,
									unsigned char *buf,
									unsigned short count)
{
	if (SPI_Flash_Wait_Ready() == SPI_FLASH_OK)
	{
		gWMode = PP;
		SPI_Flash_Write_Init(addr, buf, count);
		return SPI_FLASH_OK;
	}
	return SPI_FLASH_ERR;
}


/**
 * @brief	SPI flash initial
 * @param	id	Flash ID
 * @return	SPI_FLASH_OK/SPI_FLASH_ERR, See SPI ERROR CODE DEFINE
 * @note
 */
unsigned char SPI_Flash_Init(unsigned long id)
{
	unsigned char retry = 3;
	GPIO_InitTypeDef	GPIO_InitStructure;
	NVIC_InitTypeDef	NVIC_InitStructure;
	SPI_InitTypeDef		SPI_InitStructure;


	/* Enable the RTC Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel				= SPI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority	= IRQ_SPI1;
	NVIC_InitStructure.NVIC_IRQChannelCmd			= ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	/* I/O Setting */
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin		= 0x00
									| GPIO_Pin_5		//PA5 SCK
									| GPIO_Pin_6		//PA6 MISO
									| GPIO_Pin_7		//PA7 MOSI
									;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin		= 0x00
									| GPIO_Pin_4		//PA4 NSS
									//| GPIO_Pin_8		//PA8 Flash En
									;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_Out_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	SPI_FLASH_CS_HIGH();
	/* Clock Setting */
	//GPIO_SetBits(GPIOA, GPIO_Pin_8);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	/* SPI1 configuration */
	SPI_InitStructure.SPI_Direction			= SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode				= SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize			= SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL				= SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA				= SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS				= SPI_NSS_Soft;
	if (SYS_CLK == SYS_CLK_48M)
		SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
	else
		SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStructure.SPI_FirstBit			= SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial		= 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	/* Interrupt setting */
	SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE | SPI_I2S_IT_RXNE, DISABLE);
	/*  Enable SPI1 */
	SPI_Cmd(SPI1, ENABLE);

	SPI_Flash_Status = SPI_FLASH_READY;
	
	while (retry--)
	{
		if (id == SPI_Flash_ReadID())
		{
			return SPI_FLASH_OK;
		}
	}
	

	return SPI_FLASH_ERR;
}

void SPI_Flash_Power_On(void)
{
	SPI_Flash_WakeUP();
}
void SPI_Flash_Power_Off(void)
{
	SPI_Flash_Wait_Ready();
	SPI_Flash_Sleep();
}

/**
 * @brief	SPI1 IRQ callback function
 * @param	none
 * @return	none
 * @note
 */
void SPI1_IRQ_CALLBACK(void)
{
	if (SPI_Flash_Status == SPI_FLASH_READ)
	{
		/* Read */
		if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != RESET)
		{
			*gBuf++ = SPI_I2S_ReceiveData(SPI1);
			if (--gCount == 0)
			{
				SPI_FLASH_CS_HIGH();
				SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, DISABLE);
				*pFlag_pwr_flash = 0;
				SPI_Flash_Status = SPI_FLASH_READY;
			}
			else
			{
				SPI_I2S_SendData(SPI1, Dummy_Byte);
			}
		}
	}
	if (SPI_Flash_Status == SPI_FLASH_WRITE)
	{
		/* Write */
		if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != RESET)
		{
			if (gCount-- == 0)
			{
				SPI_FLASH_CS_HIGH();
				SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
				*pFlag_spi_busy	= 0;
			}
			else
			{
				SPI_I2S_SendData(SPI1, *gBuf++);
			}
		}
		if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != RESET)
		{
			SPI_I2S_ReceiveData(SPI1);
			if (gCount == 0)
			{
				SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, DISABLE);
			}
			
		}
	}
}

void SPI_FLASH_DisadbleProtection(void)
{
        SPI_FLASH_CS_HIGH();
        /* Enable the write access to the FLASH */
        SPI_FLASH_WriteEnable();
        /* Select the FLASH: Chip Select low */
        SPI_FLASH_CS_LOW();
        SPI_FLASH_SendByte(WRSR);
        /* Send WriteAddr high nibble address byte to write to */
        SPI_FLASH_SendByte(0x00);
		
        SPI_FLASH_CS_HIGH();
}

/*******************************************************************************
* Function Name  : SPI_FLASH_PageErase
* Description    : Erases the specified FLASH page.
* Input          : PageAddr: address of the page to erase.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_PageErase(unsigned long PageAddr)
{
        /* Send write enable instruction */
        SPI_FLASH_WriteEnable();
        /* Deselect the FLASH: Chip Select high */
        SPI_FLASH_CS_HIGH();
        /* Sector Erase */ 
        /* Select the FLASH: Chip Select low */
        SPI_FLASH_CS_LOW();
        /* Send Sector Erase instruction */
        SPI_FLASH_SendByte(PE);
        /* Send PageAddr high nibble address byte */
        SPI_FLASH_SendByte((PageAddr & 0xFF0000) >> 16);
        /* Send PageAddr medium nibble address byte */
        SPI_FLASH_SendByte((PageAddr & 0xFF00) >> 8);
        /* Send PageAddr low nibble address byte */
        SPI_FLASH_SendByte(PageAddr & 0xFF);
        /* Deselect the FLASH: Chip Select high */
        SPI_FLASH_CS_HIGH();
        
        /* Wait the end of Flash writing */
        SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
* Function Name  : SPI_FLASH_WaitForWriteEnd
* Description    : Polls the status of the Write In Progress (WIP) flag in the  
*                  FLASH's status  register  and  loop  until write  opertaion
*                  has completed.  
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
u8 FlashBusy;
void SPI_FLASH_WaitForWriteEnd(void)
{
	u8 Status;
	static short TimeOutTimer = 1000;	//1ms
	FlashBusy = 0x00;
	/* Loop as long as the memory is busy with a write cycle */
	do
	{
		Status = 0;
		/* Select the FLASH: Chip Select low */
		SPI_FLASH_CS_LOW();
		
		/* Send "Read Status Register" instruction */
		SPI_FLASH_SendByte(RDSR);
		/* Send a dummy byte to generate the clock needed by the FLASH 
		and put the value of the status register in FLASH_Status variable */
		Status = SPI_FLASH_SendByte(Dummy_Byte);
		/* Deselect the FLASH: Chip Select high */
		
		SPI_FLASH_CS_HIGH();
		
		if(TimeOutTimer==0)
		{
//				FlashBusy = FlashBusyFlag;
				return;
		}
		   
	} while((Status & WIP_Flag) == SET); /* Write in progress */
        
}
/** @} */
